package gameobjects;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;

/**
 * Author: Gunnar Sigurðsson.
 * Date: 11/21/12
 * Time: 8:29 PM
 */

/**
 * The Game class contains all logic for gameplay. It initializes the objects
 * needed for a round of TicTacToe
 */
public class Game {
    /**
     * Game Board.
     */
    private Board board;

    /**
     * The Player One.
     */
    private Player p1;

    /**
     * The Player Two.
     */
    private Player p2;

    /**
     * Current round.
     */
    private int round;

    /**
     * Max number of rounds.
     */
    private final int maxRounds = 9;

    /**
     * Buffer the input from the specified input.
     */
    private BufferedReader reader;

    /**
     * Constructor for two player, and default reader will be InputStreamReader.
     * @param pP1 - Player One
     * @param pP2 - Player Two
     */
    public Game(final Player pP1, final Player pP2) {
        this.p1 = pP1;
        this.p2 = pP2;
        this.board = new Board(pP1.getMark(), pP2.getMark());
        board.generateEmptyBoard();
        reader = new BufferedReader(new InputStreamReader(System.in));
    }

    /**
     * Constructor for two player, and input buffer.
     * @param pP1 - Player Two
     * @param pP2 - Player Two
     * @param r - Buffer the input from the specified input
     */
    public Game(final Player pP1, final Player pP2, final Reader r) {
        this.p1 = pP1;
        this.p2 = pP2;
        this.board = new Board(pP1.getMark(), pP2.getMark());
        board.generateEmptyBoard();
        reader = new BufferedReader(r);
    }

    /**
     * The main logic for playing the game
     * You can continue playing for as long as you want.
     */
    public final void play() {
        boolean continuePlaying = true;

        while (continuePlaying) {
            //Initialize all the variables for the game
            round = 0;

            board.generateNumberedBoard();
            board.generateBoard();
            gameNotOver();
            continuePlaying = playAgain();
        }
    }

    /**
     * Game is not over until someone wins
     * or the rounds is <= 9 .
     */
    public final void gameNotOver() {

        Player player = null;
        while (!board.checkForWinner() && getRounds() < maxRounds) {
            // If rounds is even return player1 else return player2
            player = switchPlayer();
            round++;
            inputIsValid(player);
            board.generateBoard();
        }
        // If the while loop has exited checks if a player has won and sets
        // that player to the board and prints the name of the winner else
        // nobody has won
        if (board.checkForWinner(player)) {
            System.out.println("\nGame over " + board.getWinner().getName()
                    + " Wins!\n");
        } else {
            System.out.println("\nGame over, nobody won\n\n");
        }
    }


    /**
     * If number of rounds is even return player1
     * else return player2.
     * @return Player
     */
    public final Player switchPlayer() {
        if (getRounds() % 2 == 0) {
            return p1;
        } else {
            return p2;
        }
    }

    /**
     * Takes in a player and checks if input is valid
     * if so then sets that on the board else prints out a error message.
     * @param player - the player to turn
     */
    public final void inputIsValid(final Player player) {
        int pick = 0;
        //Checks if a player has made a valid pick
        boolean validPick = false;
        while (!validPick) {
            System.out.println("\n It's " + player.getName() + " turn");

            try {
                pick = Integer.parseInt(reader.readLine());
            } catch (IOException e) {
                e.printStackTrace();
            } catch (NumberFormatException e) {
                // Prints out error message if player has not input numbers
                // between 1-9
                System.out.println("*** \tYou can only punch in numbers "
                        + "from 1-9\t***");
            }

            validPick = board.setChecked(pick, player.getMark());

            if (!validPick) {
                //Prints out message if square is already selected
                System.out.println("Square cannot be selected. Try again...");
                board.generateBoard();
            }
        }
    }

    /**
     * Returns true if player want to play again
     * else returns false and stops the game.
     * @return boolean - return true if player will play again else false
     */
    public final boolean playAgain() {
        // If you want to play again
        System.out.println("Do you want to play again? Y for yes N for no");
        char playAgain = 'N';
        try {
            playAgain = reader.readLine().charAt(0);
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (Character.toUpperCase(playAgain) != 'Y') {
            // If not equal to 'y' or 'Y' then stop playing else generate
            // an empty board and continue playing
            return false;
        } else {
            board.generateEmptyBoard();
            System.out.print("\n\n\n");
            return true;
        }
    }

    /**
     * @return Current Game Board
     */
    public final Board getBoard() {
        return board;
    }

    /**
     * @return Current round
     */
    public final int getRounds() {
        return this.round;
    }

    /**
     * @param pRound - round to set
     */
    public final void setRounds(final int pRound) {
        this.round = pRound;
    }
}
